﻿Imports System.Threading
Imports SFDL.FTP.FTP.Exceptions

Public Class ctrFTPClient

    Private _connection_info As ConnectionInfo
    Private _file_info As FileInfo
    Private _ftp_client As New SFDL.FTP.FTP.FtpClient
    Private _download_stopped As Boolean = False
    Private _file_size As Long = 0
    Private _settings As New Settings
    Private _is_running As Boolean
    Private _max_download_speed As Integer
    Private _sfdl_filename As String = String.Empty
    Private _sfdl_description As String = String.Empty
    Private _dowload_local_filename As String = String.Empty
    Private _total_bytes_transfered As Long = 0
    Private _stop_mark_set As Boolean = False
    Private _sfdl_version As Integer
    Private _retry_count As Integer = 0
    Private _download_failed As Boolean = False
    Private _resuming_dowload As Boolean = False
    Private _resuming_base_file_size As Long = 0

    Const MAX_RETRY As Integer = 2

    'Events
    Public Event DownloadFinished(ByVal _control As ctrFTPClient, ByVal _failed As Boolean)
    Public Event UpdateStatusProgressBarValue(ByVal _value As Long, ByVal _filename As String, ByVal _reset_counter As Boolean)
    Public Event UpdateStatusSpeed(ByVal _value As Integer, ByVal _filename As String)
    Public Event StopMarkSet(ByVal _file_name As String)
    Public Event StopMarkRemoved(ByVal _file_name As String)


#Region "Properties"

    Public Property ConnectionInfo As ConnectionInfo
        Set(value As ConnectionInfo)
            _connection_info = value
        End Set
        Get
            Return _connection_info
        End Get
    End Property

    Public Property FileInfo As FileInfo
        Set(value As FileInfo)
            _file_info = value
        End Set
        Get
            Return _file_info
        End Get
    End Property

    Public Property DownloadStoppped As Boolean
        Set(value As Boolean)
            _download_stopped = value
        End Set
        Get
            Return _download_stopped
        End Get
    End Property

    Public Property Settings As Settings
        Set(value As Settings)
            _settings = value
        End Set
        Get
            Return _settings
        End Get
    End Property

    Public ReadOnly Property isRunning As Boolean
        Get
            Return _is_running
        End Get
    End Property

    Public Property GetSetSFDLFilename As String
        Set(value As String)
            _sfdl_filename = value
        End Set
        Get
            Return _sfdl_filename
        End Get
    End Property

    Public Property GetSetSFDLDesciption As String
        Set(value As String)
            _sfdl_description = value
        End Set
        Get
            Return _sfdl_description
        End Get
    End Property

    Public ReadOnly Property GetLocalFilePath As String
        Get
            Return _dowload_local_filename
        End Get
    End Property

    Public ReadOnly Property GetStopMarkSet As Boolean
        Get
            Return _stop_mark_set
        End Get
    End Property

    Public Property GetSetSFDLContainerVersion As Integer
        Set(value As Integer)
            _sfdl_version = value
        End Set
        Get
            Return _sfdl_version
        End Get
    End Property

#End Region

#Region "Invokes"

    Private Sub RefreshLabel(ByVal value As Integer)
        If Me.InvokeRequired Then
            Me.Invoke(Sub() Me.RefreshLabel(value))
        Else
            If (lbl_dl_speed_status.Text = "Abgebrochen") = False Or _download_stopped = False Then
                lbl_dl_speed_status.Text = String.Format("{0} KB/s", value)
            End If
        End If
    End Sub

    Private Sub RefreshLabelwithText(ByVal value As String)
        If Me.InvokeRequired Then
            Me.Invoke(Sub() Me.RefreshLabelwithText(value))
        Else
            lbl_dl_speed_status.Text = value
        End If
    End Sub

    Private Sub SetProgressBarMaximum(ByVal value As Integer)
        If Me.InvokeRequired Then
            Me.Invoke(Sub() Me.SetProgressBarMaximum(value))
        Else
            ProgBarStatus.Maximum = value
        End If
    End Sub

    Private Sub SetPBStatusImage_OK()
        If Me.InvokeRequired Then
            Me.Invoke(Sub() Me.SetPBStatusImage_OK())
        Else
            pb_status.Image = My.Resources.DownloadOK
        End If
    End Sub

    Private Sub SetPBStatusImage_Fail()
        If Me.InvokeRequired Then
            Me.Invoke(Sub() Me.SetPBStatusImage_Fail())
        Else
            pb_status.Image = My.Resources.DownloadFail
        End If
    End Sub

    Private Sub SetPBStatusImage_Warning()
        If Me.InvokeRequired Then
            Me.Invoke(Sub() Me.SetPBStatusImage_Warning())
        Else
            pb_status.Image = My.Resources.DownloadAbort
        End If
    End Sub

    Private Sub RefreshProgressBar(ByVal value As Integer)
        If Me.InvokeRequired Then
            Me.Invoke(Sub() Me.RefreshProgressBar(value))
        Else

            Dim _percent As Integer = 0

            _total_bytes_transfered += value

            If Not _file_size = 0 Then

                _percent = CInt((_total_bytes_transfered / _file_size) * 100)

                ProgBarStatus.Value = _percent

            End If

        End If
    End Sub

    Private Sub CheckItemEnableDisable(ByVal value As Boolean)
        If Me.InvokeRequired Then
            Me.Invoke(Sub() Me.CheckItemEnableDisable(value))
        Else
            chkActivate.Enabled = value
        End If
    End Sub

#End Region

#Region "Hilf Subs"

    ''' <summary>
    ''' Funktion zum stoppen des Downloads
    ''' </summary>

    Friend Sub StopDownload(ByVal _value As Object)

        Try

            DownloadStoppped = True

            If isRunning = True Then

                If DownloadStoppped = True Then

                    _ftp_client.CancelAsync()
                    _ftp_client.Abort()
                    _ftp_client.Close()

                End If

            Else

                Me.RefreshLabelwithText("Abgebrochen")
                Me.SetPBStatusImage_Warning()
                CheckItemEnableDisable(True)

            End If

        Catch ex As Exception
        End Try

    End Sub

#End Region

#Region "Events"


    ''' <summary>
    ''' Event welches eintritt wenn sich die Downloadgeschwindigkeit ändert
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub MaxDownloadSpeedChangedEvent(_value As Integer)
        _max_download_speed = _value
    End Sub

    ''' <summary>
    ''' Event welches eintritt wenn sich der Fortschritt des Downloads ändert
    ''' </summary>

    Private Sub FileTransferProgress(sender As Object, e As FTP.FTP.TransferProgressEventArgs)

        Try

            If DownloadStoppped = False Then

                Me.RefreshProgressBar(e.BytesTransferred)
                Me.RefreshLabel(e.KilobytesPerSecond)

                RaiseEvent UpdateStatusProgressBarValue(e.BytesTransferred, _file_info.GetSetFileName, False)
                RaiseEvent UpdateStatusSpeed(e.KilobytesPerSecond, _file_info.GetSetFileName)

            End If

            Application.DoEvents()

        Catch ex As Exception
        End Try

    End Sub

    ''' <summary>
    ''' Event welches eintritt wenn sich der Download abgeschlossen ist
    ''' </summary>

    Private Sub FileTransferComplete(sender As Object, e As FTP.FTP.TransferCompleteEventArgs)

        Try

            If DownloadStoppped = False Then

                Me.RefreshLabelwithText("Abgeschlossen")
                CheckItemEnableDisable(False)
                Me.SetPBStatusImage_OK()

            End If

        Catch ex As Exception

        End Try

    End Sub

#End Region

    Friend Sub Download(ByVal _state As Object)

        Dim _tools As New Tools

        Try

            SetUpAndConnect()

            ChangeDirectory()

            _dowload_local_filename = _tools.GetDownloadFilePath(_settings, GetSetSFDLDesciption, GetSetSFDLFilename, _file_info)

            Debug.WriteLine("Local Filename: " & _dowload_local_filename)

            UpdateFileSize()

            DownloadFile()


        Catch ex_file_complete As FileAlreadyCompletedException

            Me.RefreshProgressBar(_file_size)
            RaiseEvent UpdateStatusProgressBarValue(_file_size, _file_info.GetSetFileName, True)

            FileTransferComplete(Nothing, Nothing)

        Catch _ex_ftp As FtpException

            Dim _error_text As String

            If _download_stopped = False Then

                Select Case _ex_ftp.LastResponse.Code

                    Case FTP.FTP.FtpResponseCode.RequestedActionNotTakenFileUnavailable

                        _error_text = "Datei nicht gefunden"

                    Case FTP.FTP.FtpResponseCode.CannotOpenDataConnection

                        _error_text = "Kann Verbindung nicht öffnen"

                    Case FTP.FTP.FtpResponseCode.RequestedFileActionNotTaken

                        _error_text = "Datei nicht gefunden"

                    Case FTP.FTP.FtpResponseCode.ServiceReadyInxxxMinutes

                        _error_text = "Server voll"

                    Case 10068

                        _error_text = "Server voll"

                    Case Else

                        If _ex_ftp.Message.Contains("Could not change working directory") Then
                            _error_text = "Ordner nicht gefunden"
                        Else
                            _error_text = "Server Fehler"
                        End If

                End Select

                Me.RefreshLabelwithText(_error_text)
                CheckItemEnableDisable(False)
                Me.SetPBStatusImage_Fail()
                _download_failed = True

            Else

                Me.RefreshLabelwithText("Abgebrochen")

                'Nur wenn wir gechecked waren
                If chkActivate.Checked Then
                    CheckItemEnableDisable(True)
                End If

                Me.SetPBStatusImage_Warning()
                _download_failed = False

            End If


        Catch ex As DownloadCancledException

            Me.RefreshLabelwithText("Abgebrochen")
            Me.SetPBStatusImage_Warning()
            CheckItemEnableDisable(True)
            _download_failed = False

        Catch ex As Exception

            If DownloadStoppped = False Then

                Me.RefreshLabelwithText("Server Fehler")
                CheckItemEnableDisable(False)
                Me.SetPBStatusImage_Fail()
                _download_failed = True

            Else

                Me.RefreshLabelwithText("Abgebrochen")
                Me.SetPBStatusImage_Warning()
                CheckItemEnableDisable(True)
                _download_failed = False

            End If

        Finally

            'Verbindung Trennen /Files freigeben
            RemoveHandler _ftp_client.TransferProgress, AddressOf FileTransferProgress
            RemoveHandler _ftp_client.TransferComplete, AddressOf FileTransferComplete

            If _ftp_client.IsConnected Then

                _ftp_client.Close()

                System.Threading.Thread.Sleep(500)

                _ftp_client.Dispose()

            End If

            If DownloadStoppped = False And _download_failed = True Then

                If _retry_count <= MAX_RETRY Then

                    _retry_count += 1

                    RefreshLabelwithText("Retry (" & _retry_count.ToString & ") - Warte 3 sek.")

                    Thread.Sleep(3000)

                    Download(New Object)

                Else
                    Debug.WriteLine("Maximale Anzahl an Retries erreicht!")
                    ' RefreshLabelwithText("Fehler")
                End If

            End If

            If _tools.isValidMD5(_file_info.GetSetMD5FileHash) And DownloadStoppped = False Then

                RefreshLabelwithText("Prüfe MD5-Hash...")

                If IO.File.Exists(_dowload_local_filename) Then

                    If String.Equals(_tools.MD5FileHash(_dowload_local_filename).ToString.ToLower, _file_info.GetSetMD5FileHash) Then
                        RefreshLabelwithText("MD5-Hash OK")
                        SetPBStatusImage_OK()
                    Else
                        RefreshLabelwithText("MD5-Hash ungleich")
                        SetPBStatusImage_Warning()
                    End If
                Else
                    RefreshLabelwithText("Datei konnte nicht gefunden werden")
                    SetPBStatusImage_Warning()
                End If

            End If

            RaiseEvent UpdateStatusSpeed(0, _file_info.GetSetFileName)

            System.Threading.Thread.Sleep(100)

            CheckItemEnableDisable(True)

            RaiseEvent DownloadFinished(Me, _download_failed)

        End Try

    End Sub

    Private Sub ChangeDirectory()
        _ftp_client.ChangeDirectory(_file_info.GetSetDirectoryPath)
    End Sub

    Private Sub UpdateFileSize()


        If _ftp_client.FileTransferType = FTP.FTP.TransferType.Binary Then

            Me.RefreshLabelwithText("Aktualisiere Dateigröße...")

            'Größe der Datei holen + Testen ob die Datei existiert
            _file_size = _ftp_client.GetFileSize(_file_info.GetSetFileName)

        End If


    End Sub


    Private Sub DownloadFile()

        Dim _tools As New Tools
        Dim _resume_supported As Boolean = False

        If IO.File.Exists(_dowload_local_filename) Then

            Dim _tmp_local_fileinfo As New IO.FileInfo(_dowload_local_filename)

            If _settings.GetSetExistingFileHandling = FTP.FTP.FileAction.ResumeOrCreate Then

                If _tmp_local_fileinfo.Length <> _file_size Then

                    Me.RefreshProgressBar(_tmp_local_fileinfo.Length)
                    RaiseEvent UpdateStatusProgressBarValue(_tmp_local_fileinfo.Length, _file_info.GetSetFileName, True)

                    _resuming_base_file_size = _tmp_local_fileinfo.Length
                    _resuming_dowload = True


                Else
                    Throw New FileAlreadyCompletedException
                End If

            Else
                IO.File.Delete(_tmp_local_fileinfo.FullName)
            End If

        End If

        Me.RefreshLabelwithText("Lade Datei herunter...")

        'Datei herunterladen
        _ftp_client.GetFile(_file_info.GetSetFileName, _dowload_local_filename, _settings.GetSetExistingFileHandling)

    End Sub

    Private Sub SetUpAndConnect()

        Dim _tools As New Tools


        'Initialize New FTPClient
        _ftp_client = New FTP.FTP.FtpClient


        'Reset Varibales
        _total_bytes_transfered = 0
        _file_size = 0
        _resuming_dowload = False
        _download_failed = False
        _resuming_base_file_size = 0

        'Check if Download is Stopped
        If DownloadStoppped = True Then
            Throw New DownloadCancledException
        End If

        'Get Settings
        _settings = frm_main.GetSettings

        'Set Up FTP Client
        _ftp_client.Host = _connection_info.GetSetHost
        _ftp_client.Port = _connection_info.GetSetPort
        _ftp_client.SecurityProtocol = _connection_info.GetSetSecurityProtocol
        _ftp_client.DataTransferMode = _connection_info.GetSetTransferMode
        _ftp_client.FileTransferType = _connection_info.GetSetTransferType

        _ftp_client.IsLoggingOn = False


        'Set Encoding
        If GetSetSFDLContainerVersion >= 4 Then
            _ftp_client.CharacterEncoding = _tools.ConvertEncodingEnumToEncoding(_connection_info.GetSetCharacterEncoding)
        Else
            _ftp_client.CharacterEncoding = System.Text.Encoding.Default
        End If

        'Set Maximum Downloadspeed
        _ftp_client.MaxDownloadSpeed = _max_download_speed

        'Version Fix älter als SFDL Version 3
        If _file_info.GetSetDirectoryRoot Is Nothing Then
            _file_info.GetSetDirectoryRoot = String.Empty
        End If

        'Declare Client to "running"
        _is_running = True

        Me.RefreshLabelwithText("Verbinde...")

        If _connection_info.GetSetAuthRequired Then
            _ftp_client.Open(_connection_info.GetSetUsername, _connection_info.GetSetPassword)
        Else
            _ftp_client.Open("anonymous", "")
        End If

        'Add Filetransfer Handler
        AddHandler _ftp_client.TransferProgress, AddressOf FileTransferProgress
        AddHandler _ftp_client.TransferComplete, AddressOf FileTransferComplete

    End Sub

    Private Sub ctrFTPClient_Load(sender As Object, e As EventArgs) Handles MyBase.Load

        AddHandler frm_main.MaxDownloadSpeedChanged, AddressOf MaxDownloadSpeedChangedEvent

        Me.Dock = DockStyle.Fill

    End Sub

End Class
